/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.metadata.service.dev.core.manager;

import com.fasterxml.jackson.databind.JsonNode;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.exception.ErrorCodes;
import com.inspur.edp.lcm.metadata.api.exception.LcmMetadataDevException;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.common.context.RuntimeContext;
import com.inspur.edp.lcm.metadata.core.MetadataProjectCoreService;
import com.inspur.edp.lcm.metadata.core.persistence.RepositoryFactory;
import com.inspur.edp.lcm.metadata.core.thread.BoGraphThreadLocalHolder;
import com.inspur.edp.metadata.service.dev.core.cache.GspMetadataInProjectCache;
import com.inspur.edp.metadata.service.dev.core.entity.GspMetadataCache;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.util.Pair;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 设计时元数据工程下元数据列表管理
 */
public class MetadataInProjectManager {

    private static final FileServiceImp fileService = new FileServiceImp();
    private static final MetadataProjectCoreService metadataProjectCoreService = new MetadataProjectCoreService();

    private MetadataInProjectManager() {
    }

    /**
     * 获取工程下元数据缓存结构
     *
     * @param projectPath 工程路径
     * @return 工程下所有元数据缓存结构
     */
    public static List<GspMetadataCache> getMetadataCacheListInProject(String projectPath) {
        return getMetadataListInProjectStream(projectPath).collect(Collectors.toList());
    }

    /**
     * 查询指定元数据工程下所有元数据(无content)
     *
     * @param projectPath 工程路径
     * @return 工程下所有元数据列表(无content)
     */
    public static List<GspMetadata> getMetadataListInProject(String projectPath) {
        return getMetadataListInProject(projectPath, null);
    }

    /**
     * 查询指定元数据工程下所有元数据(无content)
     *
     * @param projectPath 工程路径
     * @return 工程下所有元数据列表(无content)
     */
    public static List<GspMetadata> getMetadataListInProject(String projectPath, Collection<String> metadataPostfixes) {
        Stream<GspMetadataCache> stream = getMetadataListInProjectStream(projectPath);

        if (!CollectionUtils.isEmpty(metadataPostfixes)) {
            // 预处理元数据类型过滤条件, 均处理为小写以实现元数据类型大小写不敏感
            Set<String> metadataPostfixSet = metadataPostfixes.stream()
                    .filter(Objects::nonNull).map(StringUtils::lowerCase).collect(Collectors.toSet());
            stream = stream.filter(metadataCache -> metadataPostfixSet.contains(metadataCache.getFileExtension()));
        }

        return stream.map(gspMetadataCache->{
            GspMetadata metadata = gspMetadataCache.getMetadataWithoutContent();
            metadata.getHeader().setI18nName(RuntimeContext.getLanguage());
            return metadata;
        }).collect(Collectors.toList());
    }

    /**
     * 查询指定元数据工程下所有元数据(携带content)
     *
     * @param projectPath 工程路径
     * @return 工程下所有元数据列表(有content)
     */
    public static List<GspMetadata> getMetadataWithContentListInProject(String projectPath) {
        Stream<GspMetadataCache> metadataListInProjectStream = getMetadataListInProjectStream(projectPath);
        return metadataListInProjectStream.map(GspMetadataCache::getMetadataWithContent).collect(Collectors.toList());
    }

    /**
     * 获取元数据工程下元数据缓存结构流
     *
     * @param projectPath 元数据工程路径
     * @return 符合条件的元数据列表
     */
    private static Stream<GspMetadataCache> getMetadataListInProjectStream(String projectPath) {
        // 检查元数据工程路径是否为空
        if (StringUtils.isBlank(projectPath)) {
            return Stream.empty();
        }
        // 获取所有工程下元数据并更新缓存内容
        Map<String, GspMetadataCache> gspMetadataCacheMap = refreshMetadataListInProjectCache(projectPath);

        if (CollectionUtils.isEmpty(gspMetadataCacheMap)) {
            return Stream.empty();
        }
        return gspMetadataCacheMap.values().stream();
        // 若元数据类型需求不为空,过滤出符合条件的元数据列表
    }


    private static Map<String, GspMetadataCache> refreshMetadataListInProjectCache(String absolutePath) {
        // 处理projectPath入参
        String projectPath = FileServiceImp.handlePath(metadataProjectCoreService.getProjPath(absolutePath));
        if (StringUtils.isBlank(projectPath)) {
            throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0004, projectPath);
        }
        // 获取缓存中记录的 工程下元数据列表
        Map<String, GspMetadataCache> gspMetadataCacheMap = GspMetadataInProjectCache.computeIfAbsent(projectPath);
        // 添加线程变量，如果存在则返回缓存中的数据，不在操作io
        if(BoGraphThreadLocalHolder.contains(projectPath)){
            return gspMetadataCacheMap;
        }
        // 记录缓存中的元数据key集合
        Set<String> mdPathSet = new HashSet<>(gspMetadataCacheMap.keySet());

        //获取工程下的元数据列表
        List<String> mdFilePathList = new ArrayList<>();
        RepositoryFactory.getInstance().getMetadataRepository().getMetadatasUnderDir(mdFilePathList, projectPath);

        // 处理为全小写
        Set<String> mdFilePostfix = Utils.getMetadataPostfixTypes().stream().map(String::toLowerCase).collect(Collectors.toSet());

        // 遍历元数据文件列表,更新缓存内结果
        for (String mdFilePath : mdFilePathList) {
            // 获取元数据文件修改时间
            File metadataFile = new File(mdFilePath);
            String extension = StringUtils.lowerCase(fileService.getExtension(metadataFile));
            // 过滤元数据文件类型
            if (!mdFilePostfix.contains(extension)) {
                continue;
            }

            GspMetadataCache gspMetadataCache = gspMetadataCacheMap.get(mdFilePath);
            long fileLastModified = metadataFile.lastModified();
            // 缓存key集合中删除此文件路径(集合中仅保留相对缓存被删除的文件列表)
            mdPathSet.remove(mdFilePath);
            if (gspMetadataCache == null || fileLastModified != gspMetadataCache.getCacheModifyTime()) {
                Pair<JsonNode, GspMetadata> metadataPair = RepositoryFactory.getInstance().getMetadataRepository().getMetadataWithoutContentAndContent(mdFilePath);

                gspMetadataCacheMap.put(mdFilePath, new GspMetadataCache(metadataPair.getSecond(), metadataPair.getFirst(), fileLastModified, extension, mdFilePath));
            }
        }
        // 缓存的元数据对应文件不存在时,将缓存内容删除
        for (String mdpath : mdPathSet) {
            gspMetadataCacheMap.remove(mdpath);
        }
        BoGraphThreadLocalHolder.put(projectPath);
        return gspMetadataCacheMap;
    }
}

